#pragma once

#include "type_traits.hpp"

namespace zuck
{
    template<int N, int I>
    class getNextN {
    public:
        inline static int constexpr n = (N % I || I >= N ? N : 0);
    };

    template<int N, int I>
    class getNextI {
    public:
        inline static int constexpr i = (I * I > N ? 0 : I + 1);
    };

    template<int N, int I>
    class is_prime_impl {
    public:
        inline static bool constexpr value = is_prime_impl<getNextN<N, I>::n, getNextI<N, I>::i>::value;
    };

    template<int N>// data
    class is_prime_impl<N, 0> {
    public:
        inline static bool constexpr value = true;
    };

    template<int i>// divide
    class is_prime_impl<0, i> {
    public:
        inline static bool constexpr value = false;
    };

    template<int N>
    class is_prime {
    public:
        inline static bool constexpr value = is_prime_impl<N, 2>::value;
    };

    template<int N>
    class calc_sum {
    public:
        inline static int constexpr value = N + calc_sum<N - 1>::value;
    };

    template<>
    class calc_sum<0> {
    public:
        inline static int constexpr value = 0;
    };

    template<int N>
    class prime_sum {
    public:
        inline static int constexpr sum = N * is_prime<N>::value + prime_sum<N - 1>::sum;
    };

    template<>
    class prime_sum<1> {
    public:
        inline static int constexpr sum = 0;
    };


    template<size_t n>
    class fib {
    public:
        inline static constexpr size_t value = fib<n - 1>::value + fib<n - 2>::value;
    };

    template<>
    class fib<1> {
    public:
        inline static constexpr size_t value = 0;
    };

    template<>
    class fib<2> {
    public:
        inline static constexpr size_t value = 1;
    };


    template<typename Integer = void>
    class is_even {
    public:
        static_assert(is_integral_v<Integer>, "typename \"integer\" must be a integer type");

        [[nodiscard]] constexpr bool operator()(const Integer& val)const noexcept
        {
            return static_cast<bool>(!(val & 1));
        }
    };
    
    template<>
    class is_even<void> {
    public:
        template<typename Integer, zuck::enable_if_t<zuck::is_integral_v<Integer>, int*> = null>
        [[nodiscard]] constexpr bool operator()(const Integer& val)const noexcept
        {
            return static_cast<bool>(!(val & 1));
        }
    };
}

